package io;

import info.Recommendation;

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

import org.jdom2.DataConversionException;
import org.jdom2.Element;
import org.jdom2.JDOMException;



/**
 * This class provides access to Recommendation data for the peer review system.
 * 
 * @author Lun-Wei Chang
 * 
 * @version March 11, 2014
 * 
 */
public class RecommendationAccess extends AbstractAccess {
  
  /** Name of the XML's file. */
  private static final String XMLFILE = "Rec.xml";
  
  /** A constant for the recommendation id attribute in the XML file. */
  private static final String ATTRIBUTE_ID = "id";
  /** A constant for the recommendation id element in the XML file. */
  private static final String REC_ID = "RecID";
  /** A constant for the recommendation Subprogram Chair in the XML file. */
  private static final String REC_SPC = "RecSubprogramChair";
  /** A constant for the recommendation's Conference in the XML file. */
  private static final String REC_CONFERENCE = "RecConference";
  /** A constant for the recommendation's manuscript title in the XML file. */
  private static final String REC_MANUSCRIPT_TITLE = "RecManuTitle";
  /** A constant for the recommendation's manuscript author in the XML file. */
  private static final String REC_MANUSCRIPT_AUTHOR = "RecManuAuthor";
  /** A constant for the recommendation summary element in the XML file. */
  private static final String REC_SUMMARY = "RecSummary";
  /** A constant for the recommendation rationale element in the XML file. */
  private static final String REC_RATIONALE = "RecRationale";

  
  /** 
   * The constructor to instantiate the RecommendationAccess object.
   * @throws IOException If there is a problem establishing connectivity to the file.
   * @throws JDOMException If the file cannot be correctly parsed.
   */
  public RecommendationAccess() throws IOException, JDOMException {
    super(XMLFILE);
  }
  
  /** 
   * Returns a specific Recommendation.
   * @param the_id The identification number unique to each Recommendation. 
   * @return A single Recommendation.
   */
  public Recommendation getRec(final long the_id) {
    Recommendation r = null;
    final Element rootnode = getDoc().getRootElement();
    final List<Element> list = rootnode.getChildren(REC_ID);
    for (Element e : list) {
      try {
        if (e.getAttribute(ATTRIBUTE_ID).getLongValue() == the_id) {
          r = dataToRec(e);
          break;
        }
      } catch (final DataConversionException err) {
        err.printStackTrace();
      }
    }
    return r;
  }
  
  /** 
   * Returns a list of Recommendations. 
   * @return A list of all the Recommendations.
   */
  public List<Recommendation> getAllRecs() {
    final List<Recommendation> recommendations = new ArrayList<Recommendation>();

    final Element rootnode = getDoc().getRootElement();
    final List<Element> list = rootnode.getChildren();
    for (Element e : list) {
      recommendations.add(dataToRec(e));
    }
    
    return recommendations;
  }
  
  /**
   * Writes a new Recommendation entry into the database.
   * 
   * @param the_r A new Recommendation.
   * @return true if the operation was a success; false otherwise.
   */
  public boolean createRec(final Recommendation the_r) {
    boolean result = true;

    final Element rootnode = getDoc().getRootElement();
    final Element e = recToData(the_r, false);
    rootnode.addContent(e);

    // If the write operation has failed, then remove the node so that
    // subsequent write operations do not include this node.
    result = writeToDatabase();
    if (!result) {
      e.detach();
    }

    return result;
  }

  /**
   * Writes over a pre-existing Recommendation in the database.
   * 
   * @param the_r The Recommendation data to write into the database.
   * @return true if the Recommendation to edit was found and overwritten;
   *         false otherwise.
   */
  public boolean editRec(final Recommendation the_r) {
    boolean result = false;

    final Element rootnode = getDoc().getRootElement();
    final List<Element> list = rootnode.getChildren();
    Element old_element = null;
    final Element new_element = recToData(the_r, true);
    for (Element e : list) {
      if (Long.parseLong(e.getAttributeValue(ATTRIBUTE_ID)) == the_r.getID()) {
        old_element = e.detach();
        rootnode.addContent(new_element);
        result = true;
        break;
      }
    }

    // If the write operation has failed, then remove the node so that
    // subsequent write operations do not include this node.
    if (result) {
      result = writeToDatabase();
      if (!result) {
        rootnode.addContent(old_element);
        new_element.detach();
      }
    }

    return result;
  }

  /**
   * Deletes a recommendation from the database.
   * @param the_id The ID of the recommendation.
   * @return true if the operation was a success; false otherwise.
   */
  public boolean deleteRec(final long the_id) {
    boolean result = false;

    final Element rootnode = getDoc().getRootElement();
    final List<Element> list = rootnode.getChildren();
    Element element = null;
    for (Element e : list) {
      if (Long.parseLong(e.getAttributeValue(ATTRIBUTE_ID)) == the_id) {
        element = e.detach();
        result = true;
        break;
      }
    }
    if (result) {
      result = writeToDatabase();
      if (!result) {
        rootnode.addContent(element);
      }
    }

    return result;
  }

  /**
   * Translates the XML element data into a Recommendation object.
   * 
   * @param the_e The element that contains Recommendation data.
   * @return a Recommendation object.
   */
  private Recommendation dataToRec(final Element the_e) {
    final long id = Long.parseLong(the_e.getAttributeValue(ATTRIBUTE_ID));
    
    final List<String> fields = new ArrayList<String>();
    fields.add(the_e.getChildText(REC_SPC));
    fields.add(the_e.getChildText(REC_CONFERENCE));
    fields.add(the_e.getChildText(REC_MANUSCRIPT_AUTHOR));
    fields.add(the_e.getChildText(REC_MANUSCRIPT_TITLE));
    
    int summary = 0;
    final String str = the_e.getChildText(REC_SUMMARY);
    if (isNonemptyString(str)) {
      summary = Integer.parseInt(str);
    }
    
    final String rationale = the_e.getChildText(REC_RATIONALE);
    
    final Recommendation r = new Recommendation(id,
                                                fields,
                                                summary,
                                                rationale);
    return r;
  }

  /**
   * Translates the Recommendation object into XML element data.
   * 
   * @param the_r The Recommendation object.
   * @param the_edit True if this is an edit operation; false if it is a new
   *          entry.
   * @return an element containing the Recommendation data.
   */
  private Element recToData(final Recommendation the_r, final boolean the_edit) {
    final Element e = new Element(REC_ID);

    if (!the_edit) {
      final long id = getNextID();
      e.setAttribute(ATTRIBUTE_ID, Long.toString(id));
      the_r.setID(id);
    } else {
      e.setAttribute(ATTRIBUTE_ID, Long.toString(the_r.getID()));
    }
    
    e.addContent(new Element(REC_SPC).setText(the_r.getSubprogramChairName()));
    e.addContent(new Element(REC_CONFERENCE).setText(the_r.getConference()));
    e.addContent(new Element(REC_MANUSCRIPT_AUTHOR).setText(the_r.getManuscriptAuthor()));
    e.addContent(new Element(REC_MANUSCRIPT_TITLE).setText(the_r.getManuscriptTitle()));
    
    e.addContent(
        new Element(REC_SUMMARY).setText(Integer.toString(the_r.getSummaryRating())));
    
    e.addContent(new Element(REC_RATIONALE).setText(the_r.getRationale()));
    
    return e;
  }
  
  

}
